/*
 * Created on May 24, 2005
 */
package org.flexdock.perspective;

import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Point;

import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

import org.flexdock.docking.Dockable;
import org.flexdock.docking.DockingConstants;
import org.flexdock.docking.DockingManager;
import org.flexdock.docking.DockingPort;
import org.flexdock.docking.defaults.DefaultDockingStrategy;
import org.flexdock.docking.event.DockingEvent;
import org.flexdock.docking.event.DockingListener;
import org.flexdock.docking.floating.frames.DockingFrame;
import org.flexdock.docking.state.DockingPath;
import org.flexdock.docking.state.DockingState;
import org.flexdock.docking.state.FloatManager;
import org.flexdock.docking.state.MinimizationManager;
import org.flexdock.docking.state.tree.SplitNode;
import org.flexdock.util.DockingUtility;
import org.flexdock.util.RootWindow;
import org.flexdock.util.SwingUtility;

/**
 * @author Christopher Butler
 */
public class DockingStateListener extends DockingListener.Stub {

	private boolean isEnabled = true;

	public synchronized boolean isEnabled() {
		return isEnabled;
	}

	public synchronized void setEnabled(boolean isEnabled) {
		this.isEnabled = isEnabled;
	}

	public void dockingComplete(final DockingEvent dockingEvent) {
		if (!isEnabled()) {
			return;
		}

		Dockable dockable = dockingEvent.getDockable();
		DockingState info = getDockingState(dockable);
		// if docking has just completed, then we cannot be in a minimized state
		info.setMinimizedConstraint(MinimizationManager.UNSPECIFIED_LAYOUT_CONSTRAINT);

		// update the floating state
		RootWindow window = RootWindow.getRootContainer(dockable.getComponent());
		FloatManager floatManager = DockingManager.getLayoutManager().getFloatManager();
		Component frame = window == null ? null : window.getRootContainer();
		if (frame instanceof DockingFrame) {
			String groupId = ((DockingFrame) window.getRootContainer()).getGroupName();
			floatManager.addToGroup(dockable, groupId);
		} else {
			floatManager.removeFromGroup(dockable);
		}

		EventQueue.invokeLater(new Runnable() {
			public void run() {
				updateState(dockingEvent);
			}
		});
	}

	public void undockingStarted(DockingEvent evt) {
		if (isEnabled()) {
			updateState(evt);
		}
	}

	public void updateState(Dockable dockable) {
		if (dockable == null)
			return;

		if (DockingUtility.isEmbedded(dockable)) {
			updateEmbedded(dockable);
		}
	}

	private void updateEmbedded(Dockable dockable) {
		// 以下代码会导致在主窗口 show 出来之前设置的 dock 状态异常，从而无法进行后面的 DockingPath 处理
		// 暂时不明白意图，先注释掉
//        if(!dockable.getComponent().isValid())
//            return;

		try {
			updateCenterPoint(dockable);
			updateDockingPath(dockable);
			updateRelative(dockable);
		} catch (Exception e) {
			System.out.println(e);
		}
	}

	private void updateState(DockingEvent evt) {
		updateState(evt.getDockable());
	}

	private float getSplitPaneRatio(Dockable dockable, String region) {
		// check to see if the dockable was in a split layout.  if so, get the deepest split
		// node we can find so we can grab the split proportion percentage.
		SplitNode lastSplitNode = DockingPath.createNode(dockable);
		if (lastSplitNode != null) {
			return lastSplitNode.getPercentage();
		}

		// if we couldn't determine the splitPane ratio using the DockingPath above, then
		// try the regionInsets
		Float ratioObject = dockable.getDockingProperties().getRegionInset(region);
		if (ratioObject != null) {
			return ratioObject.floatValue();
		}
		// if we still can't find a specified splitPane percentage, then use
		// the default value
		return DockingManager.getDefaultSiblingSize();
	}

	private void updateCenterPoint(Dockable dockable) {
		// get the center point of the dockable
		Component comp = dockable.getComponent();
		Point p = new Point(comp.getWidth() / 2, comp.getHeight() / 2);

		// convert it to a location on the rootPane
		RootWindow window = RootWindow.getRootContainer(comp);
		Container contentPane = window.getContentPane();
		p = SwingUtilities.convertPoint(comp, p, contentPane);

		// now, convert to a proportional location on the rootPane
		float x = (float) p.x / (float) contentPane.getWidth() * 100f;
		float y = (float) p.y / (float) contentPane.getHeight() * 100f;
		p.x = Math.round(x);
		p.y = Math.round(y);

		// store the center point
		DockingState info = getDockingState(dockable);
		info.setCenter(p);
	}

	private void updateDockingPath(Dockable dockable) {
		DockingState info = getDockingState(dockable);
		DockingPath path = DockingPath.create(dockable);
		info.setPath(path);
	}

	private void updateRelative(Dockable dockable) {
		DockingState info = getDockingState(dockable);
		DockingPort port = dockable.getDockingPort();

		// don't update relative info for tabbed layout, since we
		// technically have more than one relative.
		if (port.getDockedComponent() != dockable.getComponent())
			return;

		Component comp = ((Component) port).getParent();
		// if we're not inside a split pane, then there is no relative
		if (!(comp instanceof JSplitPane)) {
			setNullRelative(info);
			return;
		}

		JSplitPane splitPane = (JSplitPane) comp;
		Component siblingComp = SwingUtility.getOtherComponent(splitPane, (Component) port);
		if (!(siblingComp instanceof DockingPort)) {
			setNullRelative(info);
			return;
		}

		Component otherDocked = ((DockingPort) siblingComp).getDockedComponent();
		Dockable sibling = otherDocked instanceof JSplitPane || otherDocked instanceof JTabbedPane ? null : DockingManager.getDockable(otherDocked);
		if (sibling == null) {
			setNullRelative(info);
			return;
		}

		// if we got here, then we are definitely sharing a split layout with another dockable.
		String region = DefaultDockingStrategy.findRegion(dockable.getComponent());
		float ratio = SwingUtility.getDividerProportion(splitPane);

		// set the relative docking info
		info.setRelativeParent(sibling);
		info.setRegion(region);
		info.setSplitRatio(ratio);

		// make the sibling aware of us
		info = getDockingState(sibling);
		info.setRelativeParent(dockable);
		info.setRegion(DockingUtility.flipRegion(region));
		info.setSplitRatio(ratio);
	}

	private void setNullRelative(DockingState info) {
		info.setRelativeParentId((String) null);
		info.setSplitRatio(DockingConstants.UNINITIALIZED);
		info.setRegion(DockingConstants.CENTER_REGION);
	}

	private DockingState getDockingState(Dockable dockable) {
		return getPerspective().getDockingState(dockable, true);
	}

	private Perspective getPerspective() {
		return PerspectiveManager.getInstance().getCurrentPerspective();
	}

}
